/****************************************************************************
  From Qt Examples
****************************************************************************/

#include "proxy.h"
#include "proxyThread.h"
#include <QDebug>
#include <QtNetwork>
#include <stdlib.h>

static bool serverLoadLessThan(ServerLoad *a, ServerLoad *b)
{
    if (a == NULL && b == NULL) return true;
    else if (a == NULL) return true;
    else if (b == NULL) return false;
    return a->getWorkLoad() < b->getWorkLoad();
}

Proxy::Proxy(QObject *parent)
    : QTcpServer(parent)
{
    simTime = 0;
    message = "";
    SDescriptor = 0;
    //connection = new QTcpSocket();
    proxyBlockSize = 0;
    timer = new QTimer();

    servers = new QList<ServerLoad*>();

}

Proxy::~Proxy()
{
    proxySocket->close();
    proxySocket->deleteLater();

    while (!servers->isEmpty()) {
        ServerLoad* target = servers->takeFirst();
        delete target;
    }

    delete servers;
}

/*
void Server::ReadCMsg()
{
    qDebug() << "MSG Recived";

    QDataStream in(connection);
    in.setVersion(QDataStream::Qt_4_0);

    if (blockSize == 0) {
        if (connection->bytesAvailable() < (int)sizeof(quint16))
            return;
        in >> blockSize;
    }

    qDebug() << "BlockSize: " << blockSize;

    if (connection->bytesAvailable() < blockSize)
        return;

    QString nextMSG;
    in >> nextMSG;

    currentMSG = nextMSG;
    qDebug() << currentMSG;
    //statusLabel->setText(currentMessage);
    //clientTextEdit->append(currentMessage);
    //getMessageButton->setEnabled(true);
    blockSize = 0;
}
*/

QString Proxy::getCMSG()
{
    return currentMSG;
}

void Proxy::setMsg(QString msg)
{
    message = msg;
}



//TESTING
void Proxy::incomingConnection(int socketDescriptor)
{
    qDebug() << socketDescriptor << " Connecting...";

    SDescriptor = socketDescriptor;

    //associate the socket descriptor with name/pass of a client to know which client to send what...
    socketDescs.append(SDescriptor);
   // connection->setSocketDescriptor(SDescriptor);

    //For reading the data from the client
   // connect(connection, SIGNAL(readyRead()), this, SLOT(ReadCMsg()));

    //Set the simTime and the timeDenomination here :: (0,1) right now
    ProxyThread *thread = new ProxyThread(SDescriptor, 0,1,message, this);

    // child thread signals
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    connect(thread,SIGNAL(clientMessage(QString)),this,SLOT(getMSG(QString)));
    connect(thread, SIGNAL(addServer(int,QString,void*)), this, SLOT(addServer(int,QString,void*)));
    connect(thread, SIGNAL(disconnectedServer(int)), this, SLOT(removeServer(int)));
    connect(thread, SIGNAL(sendLoadToProxy(int,int)),this,SIGNAL(sendLoadToDialog(int,int)));
    connect(thread, SIGNAL(sendLoadToProxy(int,int)),this,SLOT(updateServer(int,int)));
    connect(thread, SIGNAL(incomingRequest(int)), this, SLOT(balanceLoad(int)));

    // child thread slots
    connect(this,SIGNAL(sendInfo(QString)),thread,SLOT(sendtoServer(QString)));
    connect(this, SIGNAL(timeSync(unsigned int)), thread, SLOT(sendTimeSync(unsigned int)));
    connect(this, SIGNAL(serverAdded(ServerLoad*,void*)), thread, SLOT(serverAdded(ServerLoad*, void*)));
    connect(this, SIGNAL(loadBalanced(int, QHostAddress, quint16)), thread, SLOT(serverAllocated(int,QHostAddress,quint16)));
    thread->start();
}

void Proxy::getMSG(QString msg)
{
    qDebug() << "Client message: " << msg;
    message = msg;
    emit transferMessage(message);
}


/*
void Proxy::ProxyConnection(QString IP, int port)
{
    proxySocket = new QTcpSocket(this);
    proxyBlockSize = 0;
    proxySocket->abort();
    qDebug() << "Connecting to " << IP << " on port " << port;
    proxySocket->connectToHost(IP,port);
     qDebug() << "Server connected to the proxy";

}

void Proxy::sendServerData(QString serverData)
{
    //OLD CODE...
   // qDebug() << "Emitting server Data to the ProxyThread: " << serverData;
   // emit sendInfo(serverData);

    qDebug() << "Socket: "  << proxySocket->socketDescriptor();


    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_0);
    out << (quint16)0; //allocate space for the size variable
    out << serverData; // write the message
    out.device()->seek(0); //go back to front of black array
    out << (quint16)(block.size() - sizeof(quint16)); //put in the size in previously reserved space

    qDebug() << "Server writing to Proxy: " << serverData << endl;
    qDebug() << "Block: " << block;
    int test;
    test = proxySocket->write(block);//send to server
    if(test == -1)
        qDebug() << "Cant write";
    else
        qDebug() << "Server sent msg..." << endl;

}

void Proxy::recieveProxyData()
{

    qDebug() << "Server " << proxySocket->socketDescriptor() << " reading.";
    qDebug() << "Original blockSize: " << proxyBlockSize;
    QDataStream in(proxySocket);
    in.setVersion(QDataStream::Qt_4_0);

    if (proxyBlockSize == 0) {
        if (proxySocket->bytesAvailable() < (int)sizeof(quint16))
            return;
        in >> proxyBlockSize;
    }

    qDebug() << "BlockSize: " << proxyBlockSize;

    if (proxySocket->bytesAvailable() < proxyBlockSize)
        return;

    //Write the proxy data
    in >> proxyData;

     qDebug() << "Recieved the full msg from the proxy: \n" + proxyData;

    //statusLabel->setText(currentMsg);
    //connectButton->setEnabled(true);
    //tcpSocket->disconnectFromHost();
    proxyBlockSize = 0;


}
*/

void Proxy::addServer(int socketDescriptor, QString text, void* threadID)
{
    if(!serverSockets.contains(socketDescriptor)) {
        serverSockets.append(socketDescriptor);
    }
    qDebug() << "Adding server " << text;

    ServerLoad* server = new ServerLoad();
    server->setServerNumber(socketDescriptor);
    server->setServerAddress(QHostAddress(text.section("::", 0, 0)), text.section("::", 1, 1).toInt());
    server->setServerStatus("initializing");
    server->setWorkLoad(0);
    server->setAddress(QString("IP: ").append(server->getServerAddress().toString())
                                       .append("  Port: ").append(QString::number(server->getServerPort())));
    //server->setStatus("Initializing"); ? duplicate setter
    //server->setWorkLoad(0); ? called for 2nd time
    servers->append(server);

    emit serverAdded(server, threadID);
}

void Proxy::removeServer(int socketDescriptor)
{
    ServerLoad *server = findServer(socketDescriptor);
    emit serverRemoved(server);
    server->deleteLater();
    removeServerFromList(server);
    serverSockets.removeAt(serverSockets.indexOf(socketDescriptor));
}

void Proxy::updateServer(int load, int socketDescriptor)
{
    ServerLoad* model = findServer(socketDescriptor);
    if (model != NULL) {
        if (load < 0) {
            model->setWorkLoad(0);
            model->setServerStatus("Server Hardware Failure");
        }
        else {
            qDebug() << "Setting model stuff to \"Running\" and " << QString::number(load) << ".";
            model->setServerStatus("Running");
            model->setWorkLoad(load);
        }
    }
    qDebug() << "Unloaded at proxy.cpp";

    //Average workload
    int count = 0;
    int sum = 0;
    QList<ServerLoad*>::iterator iter;

    for(iter = servers->begin(); iter != servers->end(); iter++) {
        ServerLoad* current = (*iter);
        count++;
        sum += current->getWorkLoad();
    }
    emit setAverageWorkload(count, sum);

}

void Proxy::startSim()
{
    timer = new QTimer();
    timer->start(1000);
    connect(timer, SIGNAL(timeout()), this, SLOT(progressSim()));
}

void Proxy::stopSim()
{
    disconnect(timer, SIGNAL(timeout()), this, SLOT(progressSim()));
    timer->stop();
    delete timer;
}

void Proxy::progressSim()
{
    timeSync(simTime++);
}

ServerLoad* Proxy::findServer(int socketDescriptor)
{
    QList<ServerLoad*>::iterator i;

    for(i = servers->begin(); i != servers->end(); i++) {
        ServerLoad* current = (*i);
        if (current->getServerNumber() == socketDescriptor) {
            return current;
        }
    }

    return NULL;
}

void Proxy::balanceLoad(int sd)
{
    if (candidates.isEmpty()) {
        updateList();
    }

    ServerLoad* candidate = candidates.takeFirst();

    emit loadBalanced(sd, candidate->getServerAddress(), candidate->getServerPort());
}

void Proxy::updateList()
{
    ServerLoad* mins[5];

    for (int i = 0; i < 5; i++) {
        mins[i] = NULL;
    }

    int count = 0;
    QList<ServerLoad*>::iterator iter;
    for(iter = servers->begin(); iter != servers->end(); iter++) {
        ServerLoad* server = *iter;

        if (count++ < 5) {
            // populate the array first
            mins[count] = server;
        }
        else {
            // find max in array
            int max = 0;

            for (int i = 1; i < 5; i++) {
                if (mins[i] != NULL && mins[max]->getWorkLoad() < mins[i]->getWorkLoad())
                    mins[max] = mins[i];
            }

            if (mins[max] != NULL && server->getWorkLoad() < mins[max]->getWorkLoad()) {
                mins[max] = server;
            }
        }
    }

    for (int i = 0; i < 5; i++) {
        if (mins[i] != NULL) {
            candidates.append(mins[i]);
        }
    }
    qSort(candidates.begin(), candidates.end(), serverLoadLessThan);
}

void Proxy::removeServerFromList(ServerLoad* server)
{
    candidates.removeAt(candidates.indexOf(server));
    servers->removeAt(servers->indexOf(server));
}
